package files

import (
	"bufio"
	"bytes"
	"io"
	"io/fs"
	"os"
	"sort"
	"strings"

	"gitee.com/xuender/oils/hashs"
	"gitee.com/xuender/oils/ids"
	"gitee.com/xuender/oils/logs"
	"gitee.com/xuender/reader/pb"
)

// Service 文件服务.
type Service struct {
	config *pb.Config
	file   map[string]*pb.FileInfo
	list   map[string][]*pb.FileInfo
	root   []*pb.FileInfo
	as     *ArchiveService
}

// NewService 新建文件服务.
func NewService(
	config *pb.Config,
	as *ArchiveService,
) *Service {
	return &Service{
		config: config,
		file:   map[string]*pb.FileInfo{},
		list:   map[string][]*pb.FileInfo{},
		root:   []*pb.FileInfo{},
		as:     as,
	}
}

// File 文件缓存.
func (p *Service) File(id string) (*pb.FileInfo, bool) {
	fi, has := p.file[id]

	return fi, has
}

func (p *Service) getFile(id string) (io.Reader, func()) {
	info, has := p.file[id]
	if !has {
		panic("未找到:" + id)
	}

	if info.Archive != "" {
		return p.as.Reader(info)
	}

	f, err := os.Open(info.Path)
	if err != nil {
		panic(err)
	}

	return f, func() { f.Close() }
}

// GetText 获取文本内容.
func (p *Service) GetText(id string, offset int64) *pb.Text {
	f, fc := p.getFile(id)
	defer fc()

	r := bufio.NewReader(f)
	ret := &pb.Text{Offset: offset}
	buf := &bytes.Buffer{}

	for i := int64(0); i < offset+p.config.MaxTextLine; i++ {
		line, _, err := r.ReadLine()
		if err != nil {
			break
		}

		if i >= offset {
			if buf.Len() > 0 {
				buf.WriteByte('\n')
			}

			buf.Write(line)
		}
	}

	ret.Context = buf.String()
	_, _, err := r.ReadLine()
	ret.Next = err == nil

	return ret
}

// Root 设置文件系统根目录.
func (p *Service) Root(roots ...string) {
	for _, path := range roots {
		id := Hash(path)
		if _, has := p.file[id]; has {
			continue
		}

		fi := p.newFileInfo(path)

		fi.Name = path
		p.root = append(p.root, fi)
	}
}

// DelFolder 删除目录缓存.
func (p *Service) DelFolder(id string) {
	delete(p.list, id)
}

// GetFolder 目录列表.
func (p *Service) GetFolder(id string, offset int64) *pb.Folder {
	ret := &pb.Folder{Id: id}

	fi, has := p.file[id]
	if !has {
		ret.Files = p.root

		return ret
	}

	ret.Path = fi.Path

	if list, has := p.list[id]; has {
		if len(list) > int(offset+p.config.MaxFolderSize) {
			ret.Files = list[offset : offset+p.config.MaxFolderSize]
			ret.Next = true

			return ret
		}

		ret.Files = list[offset:]

		return ret
	}

	if !fi.IsDir {
		panic(fi.Path + "不是目录")
	}

	var infos []*pb.FileInfo

	logs.Debug(fi)

	if fi.Archive == "" {
		infos = p.folder(fi)
	} else {
		infos = p.as.Folder(fi, p.file)
	}

	sort.Slice(infos, func(i, j int) bool {
		if infos[i].IsDir == infos[j].IsDir {
			return strings.Compare(infos[i].Name, infos[j].Name) < 0
		}

		return infos[i].IsDir
	})

	p.list[id] = infos

	if len(infos) > int(offset+p.config.MaxFolderSize) {
		ret.Files = infos[offset : offset+p.config.MaxFolderSize]
		ret.Next = true

		return ret
	}

	ret.Files = infos[offset:]

	return ret
}

// Hash 文件路径唯一ID.
func Hash(path string) string {
	return ids.Ints(hashs.SipHash128([]byte(path))).String()
}

func (p *Service) newFileInfo(path string) *pb.FileInfo {
	f, _ := os.Stat(path)

	return p.newFileInfoByFs(f, path)
}

func (p *Service) newFileInfoByFs(f fs.FileInfo, path string) *pb.FileInfo {
	id := Hash(path)
	t, at := FileType(path)
	fi := &pb.FileInfo{
		Id:          Hash(path),
		Name:        f.Name(),
		Path:        path,
		IsDir:       f.IsDir(),
		Size:        f.Size(),
		ModTime:     f.ModTime().Unix(),
		Type:        t,
		ArchiveType: at,
	}

	if at != pb.ArchiveType_none {
		fi.IsDir = true
		fi.Archive = path
	}

	p.file[id] = fi

	return fi
}
